#include <asm/io.h>
#include <asm/atomic.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/ioctl.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/version.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <linux/securec.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/time.h>
#include <linux/jiffies.h>
#include "hisport.h"

#define DRIVER_NAME "hisport"
#define CLASS_NAME "hisport"

// i2c初始化
static int flag = 0;
static uint32_t i2c_default_sda_hold_time = 50;

// 寄存器
static uint32_t reg_n = 0;
static uint32_t flag_n = 0;
static uint32_t max_batch_length = 0;
static uint32_t max_data_length = 0;
static uint32_t max_offset_w = 0;
static uint32_t max_length_w = 0;

static struct class *hisport_class = NULL;
static struct cdev hisport_cdev;

// 主次设备号
static int h_dev_major = 510;
static int g_dev_minor = 255;

// i2c
static struct mutex g_i2c_lock;

// init
static struct mutex g_init_lock;
static uint8_t h_init_flag = 0;

// 监听线程
static uint32_t bus_n = 0;
static wait_queue_head_t h_wait_queue;
static atomic_t h_wait = ATOMIC_INIT(0);
static uint8_t h_listen = 0;

// i2c
extern int i2c_transaction(unsigned int drv_id, i2c_kmsg *msg);
extern int i2c_deploy(unsigned int drv_id, const char *algo_name, i2c_kdepl *deploy);


static int hisport_open(struct inode* inode, struct file* filp)
{
    return 0;
}

static int hisport_close(struct inode* inode, struct file* filp)
{
    return 0;
}

static uint32_t init_check(void)
{
    if (bus_n == 0 || bus_n > MAX_BUS_NUM) {
        return EPARA;
    }

    if (reg_n == 0 || reg_n > DEFAULT_REG_NUM) {
        return EPARA;
    }

    if (flag_n == 0 || flag_n > DEFAULT_FLAG_NUM) {
        return EPARA;
    }

    if (max_batch_length == 0 || max_batch_length > DEFAULT_BATCH_LEN) {
        return EPARA;
    }

    if (max_data_length == 0 || max_data_length > MAX_DATA_LEN) {
        return EPARA;
    }

    if (max_offset_w == 0 || max_offset_w > DEFAULT_OFFSET_WIDTH) {
        return EPARA;
    }

    if (max_length_w == 0 || max_length_w > DEFAULT_LENGTH_WIDTH) {
        return EPARA;
    }

    return EOK;
}

static int ioctl_init(unsigned long arg)
{
    int ret = EOK;
    STRUCT_INIT init_info = {0};

    mutex_lock(&g_init_lock);
    if (h_init_flag == 1) {
        goto exit;
    }

    // 从用户态复制数据
    if (copy_from_user(&init_info, (void *)arg, sizeof(STRUCT_INIT)) != 0) {
        ret = ECOPY;
        goto exit;
    }

    bus_n = init_info.bus_num;
    reg_n = init_info.reg_num;
    flag_n = init_info.flag_num;
    max_batch_length = init_info.max_batch_len;
    max_data_length = init_info.max_data_len;
    max_offset_w = init_info.max_offset_width;
    max_length_w = init_info.max_length_width;

    // 资源初始化检查
    ret = init_check();
    if (ret != EOK) {
        pr_err("Init parameter error: bus_num=%d, reg_num=%d, flag_num=%d, "
                 "max_batch_len=%d, max_data_len=%d, max_offset_width=%d, max_length_width=%d",
                 bus_n, reg_n, flag_n, max_batch_length,
                 max_data_length, max_offset_w, max_length_w);
        goto exit;
    }

    // 初始化等待队列
    init_waitqueue_head(&h_wait_queue);

    h_init_flag = 1;
    pr_info("Hisport init success.\n");
exit:
    mutex_unlock(&g_init_lock);
    return ret;
}

static int hisport_init_i2c(uint32_t bus_id)
{
    i2c_kdepl deploy = {0};
    deploy.flags |= I2C_FORCE_INIT;
    deploy.flags |= I2C_MASTER_INIT;
    deploy.flags |= I2C_STANDARD_SPEED;
    deploy.sda_hold = (i2c_default_sda_hold_time >> 8) & SDA_HOLD_MASK;
    deploy.sda_hold_ratio = i2c_default_sda_hold_time & SDA_HOLD_RATIO_MASK;

    return i2c_deploy(bus_id, "dw-master", &deploy);
}

static int check_write_data(const STRUCT_DATA *data)
{
    if (data->tx_buffer == NULL) {
        pr_err("Write request need send data.\n");
        return EPARA;
    }

    if (data->tx_len > max_data_length) {
        pr_err("Need send data is too long, tx_len:%d, max_data_len:%d", data->tx_len, max_data_length);
        return EPARA;
    }

    if (data->rx_buffer != NULL) {
        pr_err("Write request don't need respond data.\n");
        return EPARA;
    }
    return EOK;
}

static int check_read_data(const STRUCT_DATA *data)
{
    if (data->rx_buffer == NULL) {
        pr_err("Read request need respond data");
        return EPARA;
    }
    if (data->rx_len > max_data_length) {
        pr_err("Need read data is too long, rx_len:%d, max_data_len:%d", data->rx_len, max_data_length);
        return EPARA;
    }
    if (data->tx_buffer != NULL) {
        pr_err("Read request don't need write data.\n");
        return EPARA;
    }
    return EOK;
}

static int check_data(const STRUCT_DATA *data)
{
    int ret = EOK;
    if (data->bus_id >= bus_n) {
        pr_err("Request bus error, bus_id:%d, max_bus_id:%d", data->bus_id, bus_n);
        return EPARA;
    }

    switch (data->rw_flag) {
        case 0:
            ret = check_write_data(data);
            break;
        case 1:
            ret = check_read_data(data);
            break;
        default:
            pr_err("Request type error, rw_flag=%d", data->bus_id);
            ret = EPARA;
            break;
    }

    return ret;
}

static int copy_from_user_buffer(STRUCT_DATA *write_data, uint8_t **write_buffer)
{
    // 当tx_len为0时, 不发送data数据
    if (write_data->tx_len == 0) {
        return EOK;
    }

    *write_buffer = (uint8_t *)kmalloc(write_data->tx_len * sizeof(uint8_t), GFP_KERNEL);
    if (*write_buffer == NULL) {
        pr_err("Write_buffer is NULL");
        return EDATA;
    }

    if (copy_from_user(*write_buffer, (void *)write_data->tx_buffer, write_data->tx_len * sizeof(uint8_t)) != 0) {
        pr_err("Copy write buffer from user failed.\n");
        return ECOPY;
    }

    return EOK;
}

static i2c_kmsg msg_write(STRUCT_DATA write_data)
{
    i2c_kmsg msg = {0};
    // 分配内存
    msg.addr = (write_data.addr >> 1) & I2C_7BIT_ADDR_MASK;
    
    msg.flags = 0;
    msg.retries =  write_data.re_read_cnt;
    msg.wlen =  write_data.tx_len;
    msg.wbuf = write_data.tx_buffer;
    msg.timeout = usecs_to_jiffies(2500);
    //smbus协议的写操作需要标记msg.protocol
    return msg;
}

static int ioctl_write(unsigned long arg)
{
    int ret = EFAIL;
    uint8_t *write_buffer = NULL;
    STRUCT_DATA write_data = {0};

    if (h_init_flag == 0) {
        pr_err("Wait init");
        goto exit;
    }

    // 从用户态复制输入写数据
    if (copy_from_user(&write_data, (void *)arg, sizeof(STRUCT_DATA)) != 0) {
        ret = ECOPY;
        goto exit;
    }
    
    // 检查传入到驱动的数据
    ret = check_data(&write_data);
    if (ret != EOK) {
        pr_err("Write request fail, ret=%d", ret);
        goto exit;
    }

    // 从用户态复制write buffer
    ret = copy_from_user_buffer(&write_data, &write_buffer);
    if (ret != EOK) {
        goto exit;
    }
    write_data.tx_buffer = write_buffer;

    mutex_lock(&g_i2c_lock);
    if (flag == 0) {
        // 初始化i2c总线12~15
        int bus_id;
        for (bus_id = 12; bus_id <= 15; bus_id++) {
            ret = hisport_init_i2c(bus_id);
            if (ret != EOK) {
                pr_err("initialize i2c fail.\n");
                return ret;
            }
        }
        flag = 1;
    }
    mutex_unlock(&g_i2c_lock);

    mutex_lock(&g_i2c_lock);
    i2c_kmsg msg = msg_write(write_data);

    // 传到i2c的bus_id要加上默认值HISPORT_BUS_ID_ADD
    ret = i2c_transaction(write_data.bus_id + HISPORT_BUS_ID_ADD, &msg);
    mutex_unlock(&g_i2c_lock);

    if (ret != EOK) {
        goto exit;
    }

    atomic_inc(&h_wait);
    wake_up_interruptible(&h_wait_queue);
    atomic_dec(&h_wait);

    if (write_data.write_delay > 0) {
        msleep(write_data.write_delay);
    }
exit:
    if (write_buffer) {
        kfree(write_buffer);
    }
    return ret;
}

static i2c_kmsg msg_read(STRUCT_DATA read_data)
{
    i2c_kmsg msg = {0};
    msg.addr = (read_data.addr >> 1) & I2C_7BIT_ADDR_MASK;

    // 目前mcu走的是smbus协议，smbus协议的flags是2，其他是1
    msg.flags = (uint32_t)read_data.addr == (uint32_t)MCU_ADDR ? 2 : 1;

    msg.retries = read_data.re_read_cnt;
    msg.timeout = usecs_to_jiffies(2500);
    msg.rlen = read_data.rx_len;
    msg.rbuf = kmalloc(msg.rlen + I2C_RSERVED_EXTERN_LEN, GFP_KERNEL);
    msg.wlen = I2C_READ_TX_LEN;
    msg.wbuf = kmalloc(msg.wlen + I2C_RSERVED_EXTERN_LEN, GFP_KERNEL);

    // 读操作中wbuf是固定格式
    msg.wbuf[0] = 0x00;
    msg.wbuf[1] = read_data.offset;
    return msg;
}

static int ioctl_read(unsigned long arg)
{
    int ret;
    STRUCT_DATA read_data = {0};

    if (h_init_flag == 0) {
        pr_err("Wait init");
        return EFAIL;
    }

    if (copy_from_user(&read_data, (void *)arg, sizeof(STRUCT_DATA)) != 0) {
        return ECOPY;
    }

    ret = check_data(&read_data);
    if (ret != EOK) {
        pr_err("Write request fail, ret=%d", ret);
        return ret;
    }

    mutex_lock(&g_i2c_lock);
    if (flag == 0) {
        // 初始化i2c总线12~15
        int bus_id;
        for (bus_id = 12; bus_id <= 15; bus_id++) {
            ret = hisport_init_i2c(bus_id);
            if (ret != EOK) {
                pr_err("initialize i2c fail.\n");
                return ret;
            }
        }
        flag = 1;
    }
    mutex_unlock(&g_i2c_lock);

    mutex_lock(&g_i2c_lock);
    i2c_kmsg msg = msg_read(read_data);
    ret = i2c_transaction(read_data.bus_id + HISPORT_BUS_ID_ADD, &msg);
    mutex_unlock(&g_i2c_lock);

    if (ret != EOK) {
        return ret;
    }

    atomic_inc(&h_wait);
    wake_up_interruptible(&h_wait_queue);
    mutex_lock(&g_i2c_lock);

    // 将回传数据返回到用户态
    if (copy_to_user(read_data.rx_buffer, (char *)msg.rbuf, msg.rlen * sizeof(uint8_t) ) != 0) {
        pr_err("copy_to_user failed.\n");
        return EREAD;
    }
    mutex_unlock(&g_i2c_lock);

    atomic_dec(&h_wait);
    return ret;
}

static long hisport_ioctl(struct file* filp, unsigned int cmd, unsigned long arg)
{
    int ret = EPARA;
    if (filp == NULL) {
        pr_err("Invalid input parameter.\n");
        return -EINVAL;
    }

    switch (cmd) {
        case HISPORT_INIT:
            ret = ioctl_init(arg);
            break;
        case HISPORT_WRITE:
            ret = ioctl_write(arg);
            break;
        case HISPORT_READ:
            ret = ioctl_read(arg);
            break;
        default:
            pr_info("Hisport_ioctl default.\n");
            break;
    }
    return ret;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open  = hisport_open,
    .release = hisport_close,
    .unlocked_ioctl = hisport_ioctl,
};


static int __init hisport_init(void)
{
    int ret;
    dev_t devt;

    mutex_init(&g_init_lock);

    // 静态/动态分配设备号
    devt = MKDEV(h_dev_major, g_dev_minor);
    if (h_dev_major) {
        // 静态分配
        ret = register_chrdev_region(devt, 1, DRIVER_NAME);
    } else {
        // 动态分配
        ret = alloc_chrdev_region(&devt, 0, 1, DRIVER_NAME);
        h_dev_major = MAJOR(devt);
    }

    if (ret < 0) {
        pr_err("Get hisport dev fail, ret=%d, major=%d", ret, h_dev_major);
        return ret;
    }

    // 初始化并添加字符设备
    cdev_init(&hisport_cdev, &fops);
    hisport_cdev.owner = THIS_MODULE;
    ret = cdev_add(&hisport_cdev, devt, 1);
    if (ret < 0) {
        pr_err("Failed to add cdev\n");
        goto err_cdev;
    }

    // 创建设备类
    hisport_class = class_create(THIS_MODULE, CLASS_NAME);
    if (IS_ERR(hisport_class)) {
        ret = PTR_ERR(hisport_class);
        pr_err("Failed to create class.\n");
        goto err_class;
    }

    // 创建设备节点
    if (IS_ERR(device_create(hisport_class, NULL, devt, NULL, DRIVER_NAME))) {
        pr_err("Failed to create device.\n");
        ret = -1;
        goto err_device;
    }

    pr_info("Driver loaded.\n");
    return 0;

err_device:
    class_destroy(hisport_class);
err_class:
    cdev_del(&hisport_cdev);
err_cdev:
    unregister_chrdev_region(devt, 1);
    return ret;
}

static void __exit hisport_exit(void)
{
    while (1) {
        if (h_listen == 0 && atomic_read(&h_wait) == 0) {
            break;
        }
    }

    cdev_del(&hisport_cdev);
    device_destroy(hisport_class, MKDEV(h_dev_major, g_dev_minor));
    class_destroy(hisport_class);
    unregister_chrdev_region(MKDEV(h_dev_major, g_dev_minor), 1);
    pr_info("Driver unloaded.\n");
}

module_init(hisport_init);
module_exit(hisport_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("openUBMC-qemu");